由於筆者當初是用ASP.NET Core API + Blazor Server,所以會以Blazor Server示範,日後研究完Blazor WebAssembly會再將心得補上。
首先既然Component是可以重複利用的,我們在Index.razor放上兩個Counter,啟動專案(如果不想完整偵錯,可以按ctrl+F5,就會啟動不偵錯模式,啟動速度比較快,而且每次儲存檔案,Blazor都會偵測到,網頁重整就可以載入新程式了),瀏覽器上兩個Counter有各自的Click me按鈕,分別點擊後可以看到數字各別增加,代表是不同的Component,那這些數字又定義在哪裡呢?
打開Counter.razor,最上面是@page指示詞,這個稍後再說。再來是html跟一些C#程式,最後是@code區塊,這就是Blazor的奇妙之處了,@code相當於一般網頁JS做的事情諸如定義變數、實作方法、發送request到後端或是API,不過Blazor用C#編寫,這裡定義了一個私有變數currentCount,還有一個方法IncrementCount(),呼叫這方法的是Click me按鈕,每一次點擊按鈕都會使currentCount+1,而呈現結果就在p元素內。
currentCount定義的方式跟畫面呈現就是一種模型繫結(model binding),意思是資料跟畫面有綁定關係,.NET Framework的View的@model或是@Viewbag,Angular的[(ngModel)]也是同理,都是要做到資料流到畫面後,對畫面操作可以影響資料的行為。
我們來定義另一個變數myClass,給這變數一些bootstrap的class,再把變數放在button的class裡面,記住在html裡面用到C#的程式必須以@開頭,不然Blazor不知道要編譯。重整頁面可以看到按鈕的樣式變了,Blazor幫我們把myClass的值text-primary bg-warning放進button的class。
接著我們看FetchData.razor,因為螢幕不夠長的關係,我將畫面分成兩部分截圖,這裡看到了@using BlazorServer.Data,我們待會可以把這個using放進_import.razor,那麼@inject WeatherForecastService ForecastService又是什麼呢?我們先看@code區塊,看到這裡定義了WeatherForecast陣列型別的變數forecasts,且用非同步方法OnInitializedAsync呼叫了ForecastService.GetForecastAsync(DateTime.Now),將結果回傳forecasts,眼尖的人應該發現了最上面的ForecastService跟@code區塊的ForecastService一模一樣。
我們點一下GetForecastAsync()方法並按下F12,可以看到這個方法回傳的就是5個隨機產生的天氣資料陣列,html裡面有判斷forecasts是否為null,不是的話就產生一個table,裡面用foreach將forecasts的日期、攝氏、華氏及天氣狀態一一呈現出來。
前面說過Blazor只有一個網頁,其他內容都是一個個Component組成的,每次觸發事件,Server或是WebAssemlby都會將相應Component呈現在瀏覽器上,但Blazor怎麼知道現在要呈現哪個Component呢?
原因就是@page指示詞,這個指示詞相當於傳統的路由,可以看到Index.razor的@page 為"/",表示這是首頁,Counter.razor及FetchData.razor也有相應的@page指示詞。一個頁面可以有複數個@page指示詞,不過開頭一定要有斜線且用雙引號包起來,筆者曾想過建立enum集中管理不同Component的@page,可惜目前Blazor不支援這種做法。另外若兩個Component用了相同的@page,編譯階段就會出現錯誤訊息,所以也不用擔心若有重複路由Blazor會怎麼處理。
那麼左邊菜單的Home, Counter, Fetch data頁面又是在哪裡定義的呢?打開MainLayout.razor,可以看到NavMenu元素,再打開NavMenu.razor,可以看到三個NavLink Component,這些Component會被Server翻譯為瀏覽器認識的a元素,因此就算我們打開Dev tool,也只會看到a元素。
回到MainLayout.razor,可以看到@Body指示詞,這就是其他Component會放置的地方,可以說是種placeholder,再看App.razor裡面有Found及NotFound兩個Component,從字面看就知道,前者是當輸入的網址找到匹配的Component則會進入這裡,後者則是找不到匹配的Component,可以看到兩者都用了MainLayout。另外若有不同頁面要套用不同Layout,也可以自己定義。
說到這裡,我們複習一下Blazor Server是怎麼走的,可以看到跟Angular類似都是一層一層往下,管理較為方便。Blazor WebAssemlby跟Blazor Server差在Startup.cs的事情被Program.cs做了,以及_Host.cshtml跟index.html大致相等,以及缺少了appsettings.json檔案,通常會將程式跟資料庫溝通需要的連線字串放在這個檔案,可證Blazor WebAssemlby確實只是被動接收資料,而無法主動跟資料庫溝通,筆者曾試過在這裡引用EF Core,也是無法讓Blazor WebAssemlby接觸資料庫,在.NET Framework的世界是用XML格式的web.config,在.NET Core則改用JSON格式的appsettings.json。
Ref: ASP NET Core blazor project structure